﻿
#include "VertexIndexBuffer.h"
#include <memory>

VertexBuffer* VertexBuffer::create(int sizePerVertex, int vertexNumber, GLenum usage/* = GL_STATIC_DRAW*/)
{
    auto result = new (std::nothrow) VertexBuffer();

    if (result && result->init(sizePerVertex, vertexNumber, usage))
    {
        return result;
    }

    if (result)
    {
        delete result;
    }

    return nullptr;
}

VertexBuffer::VertexBuffer()
: _vbo(0)
, _sizePerVertex(0)
, _vertexNumber(0)
, _usage(0)
{
    
}

VertexBuffer::~VertexBuffer()
{
    if (glIsBuffer(_vbo))
    {
        glDeleteBuffers(1, &_vbo);
        _vbo = 0;
    }
}

bool VertexBuffer::init(int sizePerVertex, int vertexNumber, GLenum usage/* = GL_STATIC_DRAW*/)
{
    if (0 == sizePerVertex || 0 == vertexNumber)
    {
        return false;
    }
    _sizePerVertex = sizePerVertex;
    _vertexNumber = vertexNumber;
    _usage = usage;

    glGenBuffers(1, &_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ARRAY_BUFFER, getSize(), nullptr, _usage);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    return true;
}

int VertexBuffer::getSizePerVertex() const
{
    return _sizePerVertex;
}

int VertexBuffer::getVertexNumber() const
{
    return _vertexNumber;
}

bool VertexBuffer::updateVertices(const void* verts, int count, int begin)
{
    if (count <= 0 || nullptr == verts)
    {
        return false;
    }
    
    if (begin < 0)
    {
        begin = 0;
    }
    
    if (count + begin > _vertexNumber)
    {
        count = _vertexNumber - begin;
    }
    
    glBindBuffer(GL_ARRAY_BUFFER, _vbo);

    // 此操作返回GPU存储缓冲区的地址空间指针
    void* buffer = glMapBufferRange(GL_ARRAY_BUFFER, 
        begin * _sizePerVertex, 
        count * _sizePerVertex, 
        GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
    memcpy(buffer, verts, count * _sizePerVertex);

    // 使用glUnmapBuffer()来刷新整个缓冲区存在性能损失，此操作可以优化刷新缓冲区的性能消耗
    glFlushMappedBufferRange(GL_ARRAY_BUFFER, begin * _sizePerVertex, count * _sizePerVertex);
    glUnmapBuffer(GL_ARRAY_BUFFER);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    return true;
}

GLuint VertexBuffer::getVBO() const
{
    return _vbo;
}

int VertexBuffer::getSize() const
{
    return _sizePerVertex * _vertexNumber;
}

IndexBuffer* IndexBuffer::create(int sizePerIndex, int number, GLenum usage/* = GL_STATIC_DRAW*/)
{
    auto result = new (std::nothrow) IndexBuffer();

    if (result && result->init(sizePerIndex, number, usage))
    {
        return result;
    }

    if (result)
    {
        delete result;
    }

    return nullptr;
}

IndexBuffer::IndexBuffer()
: _vbo(0)
, _sizePerIndex(0)
, _indexNumber(0)
, _usage(0)
{

}

IndexBuffer::~IndexBuffer()
{
    if (glIsBuffer(_vbo))
    {
        glDeleteBuffers(1, &_vbo);
        _vbo = 0;
    }
}

bool IndexBuffer::init(int sizePerIndex, int number, GLenum usage/* = GL_STATIC_DRAW*/)
{
    if (number <= 0)
    {
        return false;
    }
    
    _sizePerIndex = sizePerIndex;
    _indexNumber = number;
    _usage = usage;
    
    glGenBuffers(1, &_vbo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, getSize(), nullptr, _usage);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    
    return true;
}

int IndexBuffer::getSizePerIndex() const
{
    return _sizePerIndex;
}

int IndexBuffer::getIndexNumber() const
{
    return _indexNumber;
}

bool IndexBuffer::updateIndices(const void* indices, int count, int begin)
{
    if (count <= 0 || nullptr == indices)
    {
        return false;
    }
    
    if (begin < 0)
    {
        begin = 0;
    }
    
    if (count + begin > _indexNumber)
    {
        count = _indexNumber - begin;
    }
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo);

    // 此操作返回GPU存储缓冲区的地址空间指针
    void* buffer = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 
        begin * _sizePerIndex, 
        count * _sizePerIndex, 
        GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
    memcpy(buffer, indices, count * _sizePerIndex);

    // 使用glUnmapBuffer()来刷新整个缓冲区存在性能损失，此操作可以优化刷新缓冲区的性能消耗
    glFlushMappedBufferRange(GL_ELEMENT_ARRAY_BUFFER, begin * _sizePerIndex, count * _sizePerIndex);
    glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    return true;
}

int IndexBuffer::getSize() const
{
    return _sizePerIndex * _indexNumber;
}

GLuint IndexBuffer::getVBO() const
{
    return _vbo;
}

void IndexBuffer::use()
{
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo);
}